package org.sq;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectInputStream.GetField;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.StreamCorruptedException;
import java.net.URL;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import android.content.Context;
import android.content.res.AssetManager;
import android.location.Location;
import android.location.LocationManager;
import android.os.Environment;
import android.util.Log;

public class LevelManager implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private static final String TAG = "sq";
	static final String KEY_ANSWER = "answer";
	static final String KEY_TITLE = "title";
	static final String KEY_LEVELS_KEY = "key_levels";
	static final String KEY_LEVEL = "level";
	static final String KEY_ITEM = "item"; // parent node
	static final String KEY_LOCATION = "location";
	static final String KEY_LATITUDE = "lat";
	static final String KEY_LONGITUDE = "lon";
	static final String QUESTS_PATH = "/data/cache/common_files";

//	private static LevelManager _sharedLevelManager;
	private Context mContext;
	private String[] _levels;
	private ArrayList<Quest> _quests;
	private HashMap<String, Quest> questsByName;
	private boolean init;
	private String questFilePath;
	
	public LevelManager(Context context){
		this.mContext = context;
		setInit(false);
	}
	
	public ArrayList<Quest> getQuests() {
		return _quests;
	}

	public void readQuests() {
		_quests = new ArrayList<Quest>();
		questsByName = new HashMap<String, Quest>();
		if (_levels != null) {
			for (String str : _levels) {
				Quest q = readQuest(str);
				if (q != null) {
					_quests.add(q);
					questsByName.put(q.getLevelName(), q);
				}
			}
		}
	}

	public Quest getQuestByName(String name) {
		return questsByName.get(name);
	}

	private Quest _currentQuest;

	// private int _currentLevelNumber;

//	public static LevelManager getInstance() {
//
//		if (_sharedLevelManager == null) {
//			_sharedLevelManager = new LevelManager();
//		}
//		return _sharedLevelManager;
//	}

	

	public boolean hasNewGameFile() {
		/*
		if (!Environment.getExternalStorageState().equals(
				Environment.MEDIA_MOUNTED)) {
			Log.d(TAG, "No SDCARD");
			return false;
		} else {
			// Find the directory for the SD Card using the API
			// *Don't* hardcode "/sdcard"
			File sdcard = Environment.getExternalStorageDirectory();
			File questFile = new File(sdcard, "sq.zip");
			return questFile.exists();
		}*/
		
		if(questFilePath == null){
			return false;
		}
		if("".equalsIgnoreCase(questFilePath)){
			return false;
		}
		File questFile = new File(questFilePath); 
		return questFile.exists();
	}

	public boolean hasSaveGameFile() {

		String[] files = mContext.getFilesDir().list();
		for (String f : files) {
			Log.d("LevelManager", "Find file: " + f);
		}

		File questFile = new File(mContext.getFilesDir(), "save.bin");
		boolean b = questFile.exists();
		questFile = null;
		return b;
	}

	public void copyNewGame() {
		try {
			//File sdcard = Environment.getExternalStorageDirectory();
			//File questFile = new File(sdcard, "sq.zip");
			File questFile = new File(questFilePath);
			ZipFile zipFile = new ZipFile(questFile);
			if (zipFile.isEncrypted()) {
				zipFile.setPassword("polnij_vpered");
			}
			String dest = new String(mContext.getFilesDir().getPath()
					+ QUESTS_PATH);
			zipFile.extractAll(dest);

			Log.d("zip", "Files extracted");
			File f = new File(mContext.getFilesDir().getPath() + QUESTS_PATH);
			_levels = f.list();
			// Arrays.sort(_levels);
			Arrays.sort(_levels, new Comparator<String>() {
				NumberFormat f = NumberFormat.getInstance();

				public int compare(String f1, String f2) {
					try {
						return Double.compare(f.parse(f1).longValue(),
								f.parse(f2).longValue());
					} catch (ParseException e) {
						throw new IllegalArgumentException(f1 + "|" + f2);
					}
				}
			});
			for (String s : _levels) {
				Log.d("LevelManager", "Find level: " + s);
			}
			readQuests();
			// Log.d("Files", "Size: "+ file.length);
			// for (int i=0; i < file.length; i++)
			// {
			// Log.d("Files", "FileName:" + file[i].getName());
			// }
		} catch (Exception e) {
			e.printStackTrace();
			Log.e("zip", e.getLocalizedMessage());
		}
	}

	public boolean readNextLevel() {
		for (Quest q : _quests) {
			if (!q.isCompleted()) {
				if (!isLevelClosed(q)) {
					_currentQuest = q;
					init = true;
					return true;
				}
			}
		}
		return false;
	}

	public boolean readFirstLevel() {
		if (_quests != null) {
			if (_quests.size() > 0) {
				_currentQuest = _quests.get(0);
				saveProgress();
				init = true;
				return true;
			}
		}

		return false;
	}

	public Location getQuestLocation() {
		return _currentQuest.getLocation();
	}
	
	public String getQuestTitle() {
		return _currentQuest.getTitle();
	}

	public String[] getLevels() {
		return _levels;
	}

	public Quest readQuest(String level) {
		Quest quest;
		File questFile = new File(mContext.getFilesDir().getPath()
				+ QUESTS_PATH + "/" + level + "/", "quest.xml");
		// Read text from file

		StringBuilder text = new StringBuilder();
		try {
			BufferedReader br = new BufferedReader(new FileReader(questFile));
			String line;

			while ((line = br.readLine()) != null) {
				text.append(line);
				text.append('\n');
			}
		} catch (Exception e) {
			// You'll need to add proper error handling here
			Log.d(TAG, "error" + e);
		}
		Log.d(TAG, "" + text);

		try {
			/** Handling XML */
			SAXParserFactory spf = SAXParserFactory.newInstance();
			SAXParser sp = spf.newSAXParser();
			XMLReader xr = sp.getXMLReader();
			MyXMLHandler myXMLHandler = new MyXMLHandler();
			/** Send URL to parse XML Tags */
			URL sourceUrl = new URL("file://"
					+ mContext.getFilesDir().getPath() + QUESTS_PATH + "/"
					+ level + "/quest.xml");

			/** Create handler to handle XML Tags ( extends DefaultHandler ) */

			xr.setContentHandler(myXMLHandler);
			xr.parse(new InputSource(sourceUrl.openStream()));
			quest = myXMLHandler.getQuest();

		} catch (Exception e) {
			System.out.println("XML Pasing Excpetion = " + e);
			return null;
		}
		quest.setLevelName(level);
		return quest;
	}

	public boolean checkAnswer(String answer) {
		_currentQuest.setAnswerTime(new Date());
		_currentQuest.setUserReply(answer);
		boolean right = _currentQuest.checkAnswer(answer);
		if (right) {
			_currentQuest.setPoints(1);
			_currentQuest.setCompleted(true);
		} else {
			_currentQuest.setPoints(0);
		}
		saveProgress();
		return right;
	}

	public String getCurrentLevel() {
		return _currentQuest.getLevelName();
	}

	class MyXMLHandler extends DefaultHandler {

		Boolean currentElement = false;
		String currentValue = null;
		public Quest _quest = null;

		public Quest getQuest() {
			return _quest;
		}

		/**
		 * Called when tag starts ( ex:- <name>AndroidPeople</name> -- <name> )
		 */
		@Override
		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {

			currentElement = true;

			if (localName.equals(KEY_ITEM)) {
				/** Start */
				_quest = new Quest();
			} else if (localName.equals(KEY_LOCATION)) {
				/** Get attribute value */
				// Location loc = new Location(LocationManager.GPS_PROVIDER);
				// _quest.setLocation(loc);
			}

		}

		/**
		 * Called when tag closing ( ex:- <name>AndroidPeople</name> -- </name>
		 * )
		 */
		@Override
		public void endElement(String uri, String localName, String qName)
				throws SAXException {

			currentElement = false;

			/** set value */
			if (localName.equalsIgnoreCase(KEY_ANSWER))
				_quest.addAnswerVariant(currentValue);
			if (localName.equalsIgnoreCase(KEY_TITLE))
				_quest.setTitle(currentValue);
			if (localName.equalsIgnoreCase(KEY_LEVEL))
				_quest.addLevelKey(currentValue);
			if (localName.equalsIgnoreCase(KEY_LATITUDE)) {
				try {
					Double lat = Double.parseDouble(currentValue);
					_quest.setLat(lat);
				} catch (Exception ex) {
					Log.d(TAG, "Location parse error: " + ex);
				}
			}
			if (localName.equalsIgnoreCase(KEY_LONGITUDE)) {
				try {
					Double lon = Double.parseDouble(currentValue);
					_quest.setLng(lon);
				} catch (Exception ex) {
					Log.d(TAG, "Location parse error: " + ex);
				}
			}

		}

		/**
		 * Called to get tag characters ( ex:- <name>AndroidPeople</name> -- to
		 * get AndroidPeople Character )
		 */
		@Override
		public void characters(char[] ch, int start, int length)
				throws SAXException {

			if (currentElement) {
				currentValue = new String(ch, start, length);
				currentElement = false;
			}

		}

	}

	private void copyAssets(Context context) {
		AssetManager assetManager = context.getAssets();
		String[] files = null;
		try {
			files = assetManager.list("");
		} catch (IOException e) {
			Log.e("tag", "Failed to get asset file list.", e);
		}
		File sdcard = Environment.getExternalStorageDirectory();
		File questDir = new File(sdcard, "/data/sq/1/");
		questDir.mkdirs();
		for (String filename : files) {
			InputStream in = null;
			OutputStream out = null;
			try {
				in = assetManager.open(filename);
				File outFile = new File(questDir, filename);

				out = new FileOutputStream(outFile);
				copyFile(in, out);
				in.close();
				in = null;
				out.flush();
				out.close();
				out = null;
			} catch (IOException e) {
				Log.e("tag", "Failed to copy asset file: " + filename, e);
			}
		}
	}

	private void copyFile(InputStream in, OutputStream out) throws IOException {
		byte[] buffer = new byte[1024];
		int read;
		while ((read = in.read(buffer)) != -1) {
			out.write(buffer, 0, read);
		}
	}

	public void markCurrentLevelCompleted() {
		_currentQuest.setCompleted(true);

	}

	public boolean isLevelClosed(Quest quest) {
		if (!quest.hasKeys()) {
			return false;
		}
		ArrayList<String> keys = quest.getLevelKeys();
		for (String key : keys) {
			Quest q = getQuestByName(key);
			if (!q.isCompleted()) {
				return true;
			}

		}

		return false;
	}

	public int getPointsSum() {
		int sum = 0;
		for (Quest q : _quests) {
			sum += q.getPoints();
		}
		return sum;
	}

	public void setCurrentQuest(Quest quest) {
		_currentQuest = quest;
	}

	private void saveProgress() {
		try

		{

			ObjectOutputStream oos = new ObjectOutputStream(
					new FileOutputStream(new File(mContext.getFilesDir(),
							"save.bin")));
			oos.writeObject(_quests); // write the class as an 'object'
			oos.flush(); // flush the stream to insure all of the information
							// was written to 'save.bin'
			oos.close();// close the stream
			Log.d("LevelManager",
					"LevelManager saved to: "
							+ (mContext.getFilesDir() + "/save.bin"));

		}

		catch (Exception ex) {

			Log.e("LevelManager", "Error:" + ex.getMessage());
			ex.printStackTrace();
		}

	}

	public void restoreQuests() throws StreamCorruptedException,
			FileNotFoundException, IOException, ClassNotFoundException {

		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
				new File(mContext.getFilesDir() + "/save.bin")));

		_quests = (ArrayList<Quest>) ois.readObject();
		questsByName = new HashMap<String, Quest>();
		if (_quests != null) {
			for (Quest q : _quests) {
				questsByName.put(q.getLevelName(), q);
			}
		}
		Log.d("LevelManager",
				"LevelManager restored from "
						+ (mContext.getFilesDir() + "/save.bin"));

	}

	public boolean isInit() {
		return init;
	}

	public void setInit(boolean init) {
		this.init = init;
	}

	public String getQuestFilePath() {
		return questFilePath;
	}

	public void setQuestFilePath(String questFilePath) {
		this.questFilePath = questFilePath;
	}
}
